Below is a simple hardware description from the getting started repository.
case class MyTopLevel() extends Component {
val io = new Bundle {
val cond0 = in port Bool()
val cond1 = in port Bool()
val flag = out port Bool()
val state = out port UInt(8 bits)
}
val counter = Reg(UInt(8 bits)) init 0
when(io.cond0) {
counter := counter + 1
}
io.state := counter
io.flag := (counter === 0) | io.cond1
}
It is split into chunks and explained in this section.
First, there is the structure of a SpinalHDL Component
.
A component is a piece of logic which can be instantiated (pasted) as many times as needed, and where the only accessible signals are its inputs and outputs.
case class MyTopLevel() extends Component {
val io = new Bundle {
// port definitions go here
}
// component logic goes here
}
MyTopLevel
is the name of the component.
In SpinalHDL, components use UpperCamelCase
.
Note
See also Component
for more information.
Then, the ports are defined.
val cond0 = in port Bool()
val cond1 = in port Bool()
val flag = out port Bool()
val state = out port UInt(8 bits)
Directions:
cond0
andcond1
are inputs portsflag
andstate
are outputs ports
Types:
cond0
,cond1
andflag
are 1 bit each (as 3 individual wires)state
is an 8-bit unsigned integer (a bus of 8 wires representing an unsigned integer)
Note
This syntax is only available since SpinalHDL 1.8, see io
for legacy syntax and more information.
Finally, there is the component logic:
val counter = Reg(UInt(8 bits)) init(0)
when(io.cond0) {
counter := counter + 1
}
io.state := counter
io.flag := (counter === 0) | io.cond1
counter
is a register containing an 8-bits unsigned integer, with the initial value 0. Assignments to change the state of a register are available for read-back only after the next clock sampling.
Note
Because of the presence of a register, two implicit signals are added to the component for the clock and the reset. See Reg
and clock_domain
for more information.
Then a conditional rule is described: when the input cond0
(which is in the io
bundle) is set, the counter
is incremented by one, else counter
keeps its value set in the last rule. But, there is no previous rule, you would say. With a simple signal it would be a latch, and trigger an error. But here counter
is a register, so it has a default case: it just keeps the same value.
This creates a multiplexer: the input of the counter
register can be its output or its output plus one depending on io.cond0
.
Then unconditional rules (assignments) are described:
- The output
state
is connected to the output of the registercounter
. - The output
flag
is the output of anor
gate between a signal which is true when the output of "counter
equals 0", and the inputcond1
.
Note
See also semantics
for more information.